#include "ShaderManager.h"


ShaderManager::ShaderManager(){
	m_debugState=true;
	m_nullProgram = new ShaderProgram("NULL");
}

ShaderManager::~ShaderManager(){
	 std::map <std::string,ShaderProgram *>::iterator pbegin=m_shaderPrograms.begin();
	 std::map <std::string,ShaderProgram *>::iterator pend=m_shaderPrograms.end();
	 // delete each of the shader programs first (this will clear the maps in Program)
	 // but not delete the shaders
	 while(pbegin != pend)
	 {
	   delete pbegin->second;
	   ++pbegin;
	 }
	 // now we de;ete all of the shaders ready
	 std::map <std::string,Shader *>::iterator sbegin=m_shaders.begin();;
	 std::map <std::string,Shader *>::iterator send=m_shaders.end();;

	 while(sbegin != send)
	 {
	   delete sbegin->second;
	   ++sbegin;
	 }
}


void ShaderManager::createShaderProgram(std::string name) {
	std::cerr<<"creating empty ShaderProgram "<<name.c_str()<<"\n";
	m_shaderPrograms[name]= new ShaderProgram(name);
}

void ShaderManager::attatchShader(std::string name, SHADERTYPE::ST type) {
	m_shaders[name]= new Shader(name,type);
}

void ShaderManager::attatchShaderToProgram(std::string program, std::string shader){
	// get an iterator to the shader and program
	std::map <std::string, Shader * >::const_iterator shader_=m_shaders.find(shader);
	std::map <std::string, ShaderProgram * >::const_iterator program_=m_shaderPrograms.find(program);
 
	// make sure we have a valid shader and program
	if(shader_!=m_shaders.end() && program_ !=m_shaderPrograms.end())
	{
		// now attach the shader to the program
		program_->second->attatchShader(shader_->second);
		// now increment the shader ref count so we know if how many references
		shader_->second->incrementRefCount();
 
		if (m_debugState == true)
		{
			std::cerr<<shader.c_str()<<" attached to program "<<program.c_str()<<"\n";
		}
	}
	else {std::cerr<<"Warning cant attach "<<shader.c_str() <<" to "<<program.c_str()<<"\n";}
}

GLuint ShaderManager::getProgramID(std::string name){
  std::map <std::string, ShaderProgram * >::const_iterator program=m_shaderPrograms.find(name);
  // make sure we have a valid  program
	if(program!=m_shaderPrograms.end() )
  {
    return program->second->getID();
  }
  else
  {
    std::cerr<<"Warning Program not know in use "<<name.c_str();
    return -1;
  }

}

void ShaderManager::compileShader(std::string name){
	// get an iterator to the shaders
	std::map <std::string, Shader * >::const_iterator shader=m_shaders.find(name);
	// make sure we have a valid shader
	if(shader!=m_shaders.end())
	{
		// grab the pointer to the shader and call compile
		shader->second->compile();
	}
	else {std::cerr<<"Warning shader not know in compile "<<name.c_str(); }
}

void ShaderManager::linkProgramObject(std::string name){ m_shaderPrograms[name]->link(); 
 std::map <std::string, ShaderProgram * >::const_iterator program=m_shaderPrograms.find(name);
  // make sure we have a valid  program
	if(program!=m_shaderPrograms.end() )
  {
    std::cerr<<"Linking "<<name.c_str()<<"\n";
    program->second->link();
  }
  else {std::cerr<<"Warning Program not known in link "<<name.c_str();}
}

void ShaderManager::toogleDebug(){ this->m_debugState = !m_debugState; }

void ShaderManager::use(std::string name){  
 std::map <std::string, ShaderProgram * >::const_iterator program=m_shaderPrograms.find(name);
  // make sure we have a valid  program
	if(program!=m_shaderPrograms.end() )
  {
    std::cerr<<"Shader manager Use\n";
    program->second->use();
  }
  else
  {
    std::cerr<<"Warning Program not know in use "<<name.c_str();
    glUseProgram(0);
  }
}

void ShaderManager::bindAttribute(std::string programName, GLuint index, std::string attribName){ 
  std::map <std::string, ShaderProgram * >::const_iterator program=m_shaderPrograms.find(programName);
  // make sure we have a valid  program
	if(program!=m_shaderPrograms.end() )
  {
    program->second->bindAttrib(index,attribName);
  }
  else {std::cerr<<"Warning Program not know in bindAttrib "<<programName.c_str();}
}

void ShaderManager::loadShaderSource(std::string shaderName, std::string sourceFile){
	std::map <std::string, Shader * >::const_iterator shader=m_shaders.find(shaderName);
	// make sure we have a valid shader and program
	if(shader!=m_shaders.end() )
	{
		shader->second->load(sourceFile);
	}
	else {std::cerr<<"Warning shader not know in loadShaderSource "<< shaderName.c_str();}
}

ShaderProgram * ShaderManager::operator[](const std::string &_programName){
	std::map <std::string, ShaderProgram * >::const_iterator program=m_shaderPrograms.find(_programName);
	// make sure we have a valid  program
	if(program!=m_shaderPrograms.end() )
	{
		return  program->second;
	}
	else
	{
		std::cerr<<"Warning Program not know in [] "<<_programName.c_str();
		std::cerr<<"returning a null program and hoping for the best\n";
		return m_nullProgram;
	}
}
